using System;
using System.Collections.Generic;
using TestCases.Utils;

namespace TestCases.CompareAssert.Matchers
{
    public class ObjectMatcher<T>: IObjectMatcher<T>
    {
        private readonly HashSet<T> _values = new HashSet<T>();
        private readonly HashSet<Predicate<T>> _predicates = new HashSet<Predicate<T>>(PredicateEqualityComparer<T>.Instance);

        public ObjectMatcher()
        {            
        }
        
        public ObjectMatcher(T value)
        {
            _values.Add(value);          
        }
        
        public ObjectMatcher(IEnumerable<T> values)
        {
            _values.AddAll(values);
        }
        
        public ObjectMatcher(Predicate<T> predicate)
        {
            _predicates.Add(predicate);
        }                

        public void AddValue(T value)
        {
            _values.Add(value);
        }

        public void AddValues(IEnumerable<T> values)
        {
            _values.AddAll(values);
        }
        
        public void AddPredicate(Predicate<T> predicate)
        {
            _predicates.Add(predicate);
        }

        public virtual bool IsMatch(T value)
        {
            if (_values.Contains(value))
            {
                return true;
            }
            
            foreach (var predicate in _predicates)
            {
                if (predicate(value))
                {
                    return true;
                }
            }
            return false;
        }

        public virtual void Reset()
        {
            _values.Clear();
            _predicates.Clear();
        }


        public bool Equals(ObjectMatcher<T> other)
        {
            return other != null && (ReferenceEquals(other, this) || 
                (_values.SetEquals(other._values) && _predicates.SetEquals(other._predicates)));
        }

        public override bool Equals(object obj)
        {
            return Equals(obj as ObjectMatcher<T>);
        }

        public override int GetHashCode()
        {
            return _values.CalculateHash() ^ _predicates.CalculateHash();
        }
    }
}